package com.wish.datastructure;

import com.wish.inter.datastructure.ICalculatorPlus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by suliucheng on 2021/4/14 21:39
 * Directions: 计算器，执行加减乘除幂  这个是自创计算器，不涉及括号用法  高级学习波兰计算器
 */
public class CalculatorPlus implements ICalculatorPlus {

    /**
     * 初始化一个数栈
     */
    private static Stack numStack = new Stack(5);
    /**
     * 初始化一个符号栈
     */
    private static Stack signStack = new Stack(5);

    /**
     * 提供给判断是否是运算符使用
     */
    private static String signs = "/*-+^";

    private static Map<String, Integer> signLevel = new HashMap<>(5);

    static {
        signLevel.put("+", 0);
        signLevel.put("-", 0);
        signLevel.put("*", 1);
        signLevel.put("/", 1);
        signLevel.put("^", 2);
    }


    /**
     * 计算入口
     *
     * @param explain 表达式体
     * @return 计算的值
     */
    @Override
    public Double calculate(String explain) {
        List<String> explainList = explainToStringList(explain);
        for (int i = 0; i < explainList.size(); i++) {
            String indexStr = explainList.get(i);
            if (isSign(indexStr)) {
                if (signStack.stackIsEmpty()) {
                    signStack.push(indexStr);
                } else {
                    runStack(indexStr);
                }
            } else if (isNum(indexStr)) {
                numStack.push(indexStr);
            } else {
                throw new RuntimeException("运行时异常：字符串解析错误。");
            }
        }
        while (true) {
            if (signStack.stackIsEmpty()) {
                break;
            }
            String topNum = numStack.pop();
            String underTopNum = numStack.pop();
            String topSign = signStack.pop();
            Double tempResult = doSimpleCalculate(underTopNum, topSign, topNum);
            numStack.push(String.valueOf(tempResult));
        }
        return Double.parseDouble(numStack.pop());
    }

    private void runStack(String indexStr) {
        String topSign = signStack.cloneTop();
        Integer topSignLevel = signLevel.get(topSign);
        Integer indexStrLevel = signLevel.get(indexStr);
        if (indexStrLevel > topSignLevel) {
            signStack.push(indexStr);
        } else if (indexStrLevel <= topSignLevel) {
            String topNum = numStack.pop();
            String underTopNum = numStack.pop();
            topSign = signStack.pop();
            Double tempResult = doSimpleCalculate(underTopNum, topSign, topNum);
            numStack.push(String.valueOf(tempResult));
            if (signStack.stackIsEmpty()) {
                signStack.push(indexStr);
            } else {
                runStack(indexStr);
            }
        } else {
            throw new RuntimeException("运行时异常：符号运算级别定义错误。");
        }
    }

    private Double doSimpleCalculate(String underTopNum, String topSign, String topNum) {
        Double tempResult = 0.0;

        switch (topSign) {
            case "+":
                tempResult = Double.parseDouble(underTopNum) + Double.parseDouble(topNum);
                break;

            case "-":
                tempResult = Double.parseDouble(underTopNum) - Double.parseDouble(topNum);
                break;

            case "*":
                tempResult = Double.parseDouble(underTopNum) * Double.parseDouble(topNum);
                break;

            case "/":
                tempResult = Double.parseDouble(underTopNum) / Double.parseDouble(topNum);
                break;

            case "^":
                tempResult = Math.pow(Double.parseDouble(underTopNum), Double.parseDouble(topNum));
                break;
            default:
                break;
        }
        return tempResult;
    }

    private boolean isNum(String indexStr) {
        try {
            Double.parseDouble(indexStr);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    private boolean isSign(String indexStr) {
        if (signs.contains(indexStr)) {
            return true;
        }
        return false;
    }

    @Override
    public List<String> explainToStringList(String explain) {
        int countSign = 0;
        int indexSign = 0;
        int temp = 0;
        List<String> explainList = new ArrayList<>();

        //首先计算一共有几个运算符号
        for (int i = 0; i < explain.length(); i++) {
            String each = String.valueOf(explain.charAt(i));
            if (signs.contains(each)) {
                countSign++;
            }
        }
        //截取字符串
        for (int i = 0; i < explain.length(); i++) {
            String each = String.valueOf(explain.charAt(i));
            if (signs.contains(each)) {
                indexSign++;
                if (indexSign == 1) {
                    temp = i;
                    String firstNum = explain.substring(0, i);
                    explainList.add(firstNum);
                    explainList.add(each);
                } else {
                    String firstNum = explain.substring(temp + 1, i);
                    explainList.add(firstNum);
                    explainList.add(each);
                    temp = i;
                }
                //最后一个运算符号的特殊处理
                indexLastSign(indexSign, countSign, explain, i, explainList);
            }
        }
        return explainList;
    }

    private void indexLastSign(int indexSign, int countSign, String explain, int i, List<String> explainList) {
        if (countSign == indexSign) {
            String lastNum = explain.substring(i + 1);
            try {
                Double.parseDouble(lastNum);
            } catch (NumberFormatException e) {
                throw new NumberFormatException("数字格式化异常：表达式的字符串输入异常");
            }
            explainList.add(lastNum);
        }
    }
}
